/*
 * Copyright 2003 Program of Computer Graphics, Cornell University
 *     580 Rhodes Hall
 *     Cornell University
 *     Ithaca NY 14853
 * Web: http://www.graphics.cornell.edu/
 * 
 * Not for commercial use. Do not redistribute without permission.
 */

#ifndef _H_AXISALIGNEDBOX
#define _H_AXISALIGNEDBOX

#include "Ray.h"

class AxisAlignedBox {
public:
  //note: fourth component of min and max are undefined (we only need first three)
  F32vec4 min,max;

  AxisAlignedBox() {}
  AxisAlignedBox(float x, float y, float z) { //initialize to contain specified point
    min[0] = max[0] = x; min[1] = max[1] = y; min[2] = max[2] = z; min[3] = max[3] = 0.0f;
  }
  AxisAlignedBox(const F32vec4 &pt) { min = max = pt; } //initialize to contain specified point
  AxisAlignedBox(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
    min[0] = minX; min[1] = minY; min[2] = minZ; min[3] = 0.0f;
    max[0] = maxX; max[1] = maxY; max[2] = maxZ; max[3] = 0.0f;
  }
  AxisAlignedBox(const F32vec4 &_min, const F32vec4 &_max) 
  { min = _min; max = _max; }
  void resetToEmpty() 
  { min = F32vec4(0,FLT_MAX,FLT_MAX,FLT_MAX); max = F32vec4(0,-FLT_MAX,-FLT_MAX,-FLT_MAX); }
  void addPoint(float x, float y, float z) {
    F32vec4 pt(0,z,y,x);
    min = simd_min(min,pt); max = simd_max(max,pt);
  }
  void addPoint(const F32vec4 &pt) {
    min = simd_min(min,pt); max = simd_max(max,pt);
  }
  bool containsPoint(float x, float y, float z) const {
    F32vec4 pt(0,z,y,x);
    F32vec4 test = cmpge(pt,min)&cmple(pt,max);
    return (move_mask(test)&0x07)==0x07; //ignore results from fourth components
  }
  bool containsPoint(const F32vec4 &pt) const {
    F32vec4 test = cmpge(pt,min)&cmple(pt,max);
    return (move_mask(test)&0x07)==0x07; //ignore results from fourth components
  }
  bool intersects(const AxisAlignedBox &inBox) const {
    F32vec4 test = cmple(min,inBox.max)&cmpge(max,inBox.min);
    return (move_mask(test)&0x07)==0x07; //ignore results from fourth components
  }
  bool contains(const AxisAlignedBox &inBox) const {
    F32vec4 test = cmple(min,inBox.min)&cmpge(max,inBox.max);
    return (move_mask(test)&0x07)==0x07; //ignore results from fourth components
  }
  bool isEmpty() const {
    F32vec4 test = cmpgt(min,max);
    return (move_mask(test)&0x07)!=0; //ignore results from fourth components
  }
  void expandBy(F32vec4 &amt) {
    min -= amt;
    max += amt;
  }
  void epsilonExpand(float num) { //expand box by specified number of epsilons and force dummy values to zero
    const Is32vec4 mask3Int(0,-1,-1,-1);
    const F32vec4 mask3 = _mm_castsi128_ps(mask3Int);
    F32vec4 eps = F32vec4(num)*computeEpsilon();
    min = mask3&(min-eps);
    max = mask3&(max+eps);
  }
  F32vec4 computeEpsilon() {
    const Iu32vec4 absMaskInt(0x7FFFFFFF,0x7FFFFFFF,0x7FFFFFFF,0x7FFFFFFF);
    const F32vec4 absMask = _mm_castsi128_ps(absMaskInt);
    const F32vec4 vecEps = F32vec4(FLT_EPSILON)*simd_max(max-min,simd_max(min&absMask,max&absMask));
//    return vecEps;
    return max3(vecEps);
  }
  //check if given ray intersects this bounding box and clip ray to intersection if it does
  bool clipRay(Ray *ray) const;
  bool clipRayExt(RayExt &ray) const;
};


#endif //_H_AXISALIGNEDBOX
